/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Driver Module                                  */
/*                                                                           */
/*    Copyright (C) 2011 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    I2C Driver for PIC32 series                                            */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#define USE_AND_OR
#include <drvI2C.h>
#include <krnlSystemTimer.h>
#include "drvIOConfig.h"

/*****************************************************************************/
/* I2C 3A Functions                                                           */
/*****************************************************************************/

#if defined(I2C3_CON)

///////////////////////////////////////////////////////////////////////////////
// Initialize I2C driver
void drvI2C3Init(void)
{
	// Silicon bug workaround
	I2CEnable(I2C3A, FALSE);
	krnlDelay(2);
	LATFbits.LATF4 = 0;
	krnlDelay(2);
  TRISFbits.TRISF4 = 0;		
	krnlDelay(2);
	I2CEnable(I2C3A, TRUE);
	krnlDelay(2);
  TRISFbits.TRISF4 = 1;
	krnlDelay(2);
  
	I2CConfigure(I2C3A, I2C_ENABLE_SLAVE_CLOCK_STRETCHING|I2C_ENABLE_HIGH_SPEED);
	I2CSetFrequency(I2C3A, sysPBCLK_FREQUENCY, I2C3_BAUD_RATE);
}

///////////////////////////////////////////////////////////////////////////////
// Issue start condition (true is start was acknowledged)
dosBool drvI2C3Start(drvI2CRWMode in_mode, dosByte in_address)
{
	I2C_STATUS  status;

	// send start condition
	I2CStart(I2C3A);

	// Wait for the signal to complete
	do
	{
		status = I2CGetStatus(I2C3A);
  } while ( !(status & I2C_START) );

	// Transmit the address byte
	I2CSendByte(I2C3A, (in_address << 1) | ((dosByte)in_mode));

	// Wait for the transmission to finish
	while(!I2CTransmissionHasCompleted(I2C3A));

	// return ack status	
	return I2CByteWasAcknowledged(I2C3A);
}

///////////////////////////////////////////////////////////////////////////////
// Sends Restart condition
dosBool drvI2C3Restart(drvI2CRWMode in_mode, dosByte in_address)
{
	I2C_STATUS  status;

	// send start condition
	I2CRepeatStart(I2C3A);

	// Wait for the signal to complete
	do
	{
		status = I2CGetStatus(I2C3A);
  } while ( !(status & I2C_START) );

	// Transmit the address byte
	I2CSendByte(I2C3A, (in_address << 1) | ((dosByte)in_mode));

	// Wait for the transmission to finish
	while(!I2CTransmissionHasCompleted(I2C3A));

	// return ack status	
	return I2CByteWasAcknowledged(I2C3A);
}

///////////////////////////////////////////////////////////////////////////////
// Issue stop condition
void drvI2C3Stop(void)
{
	I2C_STATUS  status;

	// Send the Stop signal
	I2CStop(I2C3A);

	// Wait for the signal to complete
	do
	{
		status = I2CGetStatus(I2C3A);
	} while ( !(status & I2C_STOP) );
}

///////////////////////////////////////////////////////////////////////////////
// Sends data byte
dosByte drvI2C3WriteByte(dosByte in_data)
{
	// Transmit the byte
	I2CSendByte(I2C3A, in_data);

  // Wait for the transmission to finish
	while(!I2CTransmissionHasCompleted(I2C3A));

	// return ack status	
	return I2CByteWasAcknowledged(I2C3A);
}

///////////////////////////////////////////////////////////////////////////////
// Reads one byte from the bus
dosByte drvI2C3ReadByte(drvI2CACK in_ack)
{
	dosByte data;
	
	// enable receiver	
	I2CReceiverEnable(I2C3A, TRUE);
	

	// wait while receiving byte
	while(!I2CReceivedDataIsAvailable(I2C3A));

	// set acknowledge
	I2CAcknowledgeByte(I2C3A, (in_ack == drvI2C_ACK)?TRUE:FALSE);

	// get data from receiver buffer
  data = I2CGetByte(I2C3A);

	// Wait till ACK/NACK sequence is over 
  while(I2C3ACONbits.ACKEN == 1);

	return data;
}

#endif


/*****************************************************************************/
/* I2C 2A Functions                                                           */
/*****************************************************************************/

#if defined(I2C2_CON)

///////////////////////////////////////////////////////////////////////////////
// Local functions
static dosBool drvI2C2SendAddress(drvI2CRWMode in_mode, dosByte in_address);
static void drvI2C2Wait(void);
static dosBool drvI2C2IsBusy(void);

///////////////////////////////////////////////////////////////////////////////
// Initialize I2C2 driver
void drvI2C2Init(void)
{
	// Set configuration
	I2CConfigure(I2C2A, I2C2_CON);

	// Set Desired Operation Frequency
	I2CSetFrequency(I2C2A, sysPBCLK_FREQUENCY, I2C2_BAUD_RATE);

	// Enable I2C
	I2CEnable(I2C2A, TRUE);
}

///////////////////////////////////////////////////////////////////////////////
// Issue start condition (true is start was acknowledged)
dosBool drvI2C2Start(drvI2CRWMode in_mode, dosByte in_address)
{
	// be sure bus is idle
	drvI2C2Wait();
	
	// start condition
	I2C2ACONbits.SEN = 1;   		// Initiate Start on SDA and SCL pins */
	drvI2C2Wait();							// Wait till Start sequence is completed

	return drvI2C2SendAddress(in_mode, in_address);
}

///////////////////////////////////////////////////////////////////////////////
// Sends Restart condition
dosBool drvI2C2Restart(drvI2CRWMode in_mode, dosByte in_address)
{
	I2C2ACONbits.RSEN = 1;   		// Initiate restart on SDA and SCL pins
  while(I2C2ACONbits.RSEN );   // Wait till Restart sequence is completed
		
	return drvI2C2SendAddress(in_mode, in_address);
}

///////////////////////////////////////////////////////////////////////////////
// Sends device address and waits for ack
static dosBool drvI2C2SendAddress(drvI2CRWMode in_mode, dosByte in_address)
{
	// address and mode
	I2C2ATRN = (in_address << 1) | ((dosByte)in_mode);
	drvI2C2Wait();

	return (I2C2ASTATbits.ACKSTAT == 0);
}

///////////////////////////////////////////////////////////////////////////////
// Issue stop condition
void drvI2C2Stop(void)
{
   I2C2ACONbits.PEN = 1;			// Initiate Stop on SDA and SCL pins
   drvI2C2Wait();						// Wait till stop sequence is completed
}

///////////////////////////////////////////////////////////////////////////////
// Sends data byte
dosByte drvI2C2WriteByte(dosByte in_data)
{
	I2C2ATRN = in_data;
	drvI2C2Wait();

	return (I2C2ASTATbits.ACKSTAT == 0);
}

///////////////////////////////////////////////////////////////////////////////
// Reads one byte from the bus
dosByte drvI2C2ReadByte(drvI2CACK in_ack)
{
	dosByte data;
	
	// read byte
	I2C2ACONbits.RCEN = 1;
	drvI2C2Wait();
  I2C2ASTATbits.I2COV = 0;
  data = I2C2ARCV;
	
	// generate ACK/NACK
	if(in_ack == drvI2C_ACK)
	{
		I2C2ACONbits.ACKDT = 0;
		I2C2ACONbits.ACKEN = 1;
	}
	else
	{
		I2C2ACONbits.ACKDT = 1;
		I2C2ACONbits.ACKEN = 1;
	}
  drvI2C2Wait();					   // Wait till ACK/NACK sequence is over
  
	return data;
}

///////////////////////////////////////////////////////////////////////////////
// True if I2C bus is busy
static dosBool drvI2C2IsBusy(void)
{
	return (I2C2ACONbits.SEN || I2C2ACONbits.RSEN || I2C2ACONbits.PEN || I2C2ACONbits.RCEN || I2C2ACONbits.ACKEN || I2C2ASTATbits.TRSTAT); 
}

///////////////////////////////////////////////////////////////////////////////
// Waits while I2C bus is busy
static void drvI2C2Wait(void)
{
	while(drvI2C2IsBusy());
}

#endif
